This commit moves Cargo onto 100% stable Rust now that the necessary fs features
have been stabilized. The tests were updated to understand when they're running
with a non-nightly compiler and disable all plugin-related tests. The only major
feature here is that the calls to `fs::set_file_times` were reimplemented
manually in the tests that Cargo has.
Cargo still requires a *nightly compiler* due to the stable features not having
made their way into the stable channel yet, but it will soon work on all of the
stable, beta, and nightly compilers once 1.1 is released!
"curl 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
"docopt 0.6.64 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "filetime 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "filetime 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"flate2 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
"git2 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"git2-curl 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
"registry 0.1.0",
"rustc-serialize 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"semver 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)",
- "tar 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tar 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
"tempdir 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"term 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
"threadpool 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
[[package]]
name = "filetime"
-version = "0.1.3"
+version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
[[package]]
name = "tar"
-version = "0.2.13"
+version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
docopt = "0.6"
env_logger = "0.3"
flate2 = "0.2"
-git2 = { version = "0.2", features = ["unstable"] }
+git2 = "0.2"
git2-curl = "0.2"
glob = "0.2"
libc = "0.1"
tempdir = "0.3"
hamcrest = { git = "https://github.com/carllerche/hamcrest-rust.git" }
bufstream = "0.1"
+filetime = "0.1"
[[bin]]
name = "cargo"
-#![cfg_attr(unix, feature(fs_ext))]
-
extern crate cargo;
extern crate env_logger;
extern crate git2_curl;
#![deny(unused)]
-#![feature(file_type, dir_entry_ext)]
#![cfg_attr(test, deny(warnings))]
#[cfg(test)] extern crate hamcrest;
}
fn rm_root(&self) -> Result<(), String> {
- if self.root.exists() {
+ if self.root.c_exists() {
rmdir_recursive(&self.root)
} else {
Ok(())
use std::sync::{Once, ONCE_INIT};
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
+use filetime::FileTime;
+
static CARGO_INTEGRATION_TEST_DIR : &'static str = "cit";
static NEXT_ID: AtomicUsize = ATOMIC_USIZE_INIT;
thread_local!(static TASK_ID: usize = NEXT_ID.fetch_add(1, Ordering::SeqCst));
fn rm_rf(&self) -> io::Result<()>;
fn mkdir_p(&self) -> io::Result<()>;
fn move_into_the_past(&self) -> io::Result<()>;
+
+ // cargo versions of the standard PathExt trait
+ fn c_exists(&self) -> bool;
+ fn c_is_file(&self) -> bool;
+ fn c_is_dir(&self) -> bool;
+ fn c_metadata(&self) -> io::Result<fs::Metadata>;
}
impl CargoPathExt for Path {
* care all that much for our tests
*/
fn rm_rf(&self) -> io::Result<()> {
- if self.exists() {
+ if self.c_exists() {
for file in fs::read_dir(self).unwrap() {
let file = try!(file).path();
- if file.is_dir() {
+ if file.c_is_dir() {
try!(file.rm_rf());
} else {
// On windows we can't remove a readonly file, and git will
Ok(()) => {}
Err(ref e) if cfg!(windows) &&
e.kind() == ErrorKind::PermissionDenied => {
- let mut p = file.metadata().unwrap().permissions();
+ let mut p = file.c_metadata().unwrap().permissions();
p.set_readonly(false);
fs::set_permissions(&file, p).unwrap();
try!(fs::remove_file(&file));
}
fn move_into_the_past(&self) -> io::Result<()> {
- if self.is_file() {
+ if self.c_is_file() {
try!(time_travel(self));
} else {
- let target = self.join("target");
- for f in try!(fs::walk_dir(self)) {
- let f = try!(f).path();
- if f.starts_with(&target) { continue }
- if !f.is_file() { continue }
- try!(time_travel(&f));
- }
+ try!(recurse(self, &self.join("target")));
}
return Ok(());
+ fn recurse(p: &Path, bad: &Path) -> io::Result<()> {
+ if p.c_is_file() {
+ time_travel(p)
+ } else if p.starts_with(bad) {
+ Ok(())
+ } else {
+ for f in try!(fs::read_dir(p)) {
+ let f = try!(f).path();
+ try!(recurse(&f, bad));
+ }
+ Ok(())
+ }
+ }
+
fn time_travel(path: &Path) -> io::Result<()> {
- let stat = try!(path.metadata());
+ let stat = try!(path.c_metadata());
- let hour = 1000 * 3600;
- let newtime = stat.modified() - hour;
+ let mtime = FileTime::from_last_modification_time(&stat);
+ let newtime = mtime.seconds() - 3600;
// Sadly change_file_times has a failure mode where a readonly file
// cannot have its times changed on windows.
- match fs::set_file_times(path, newtime, newtime) {
+ match set_file_times(path, newtime, newtime) {
Err(ref e) if e.kind() == io::ErrorKind::PermissionDenied => {}
e => return e,
}
let mut perms = stat.permissions();
perms.set_readonly(false);
try!(fs::set_permissions(path, perms));
- fs::set_file_times(path, newtime, newtime)
+ set_file_times(path, newtime, newtime)
+ }
+
+ #[cfg(unix)]
+ fn set_file_times(p: &Path, atime: u64, mtime: u64) -> io::Result<()> {
+ use std::os::unix::prelude::*;
+ use std::ffi::CString;
+ use libc::{timeval, time_t, c_char, c_int};
+
+ let p = try!(CString::new(p.as_os_str().as_bytes()));
+ let atime = timeval { tv_sec: atime as time_t, tv_usec: 0, };
+ let mtime = timeval { tv_sec: mtime as time_t, tv_usec: 0, };
+ let times = [atime, mtime];
+ extern {
+ fn utimes(name: *const c_char, times: *const timeval) -> c_int;
+ }
+ unsafe {
+ if utimes(p.as_ptr(), times.as_ptr()) == 0 {
+ Ok(())
+ } else {
+ Err(io::Error::last_os_error())
+ }
+ }
+ }
+
+ #[cfg(windows)]
+ fn set_file_times(p: &Path, atime: u64, mtime: u64) -> io::Result<()> {
+ use std::fs::OpenOptions;
+ use std::os::windows::prelude::*;
+ use winapi::{FILETIME, DWORD};
+ use kernel32;
+
+ let f = try!(OpenOptions::new().write(true).open(p));
+ let atime = to_filetime(atime);
+ let mtime = to_filetime(mtime);
+ return unsafe {
+ let ret = kernel32::SetFileTime(f.as_raw_handle() as *mut _,
+ 0 as *const _,
+ &atime, &mtime);
+ if ret != 0 {
+ Ok(())
+ } else {
+ Err(io::Error::last_os_error())
+ }
+ };
+
+ fn to_filetime(seconds: u64) -> FILETIME {
+ // FILETIME is a count of 100ns intervals, and there are 10^7 of
+ // these in a second
+ let seconds = seconds * 10_000_000;
+ FILETIME {
+ dwLowDateTime: seconds as DWORD,
+ dwHighDateTime: (seconds >> 32) as DWORD,
+ }
+ }
}
}
+
+ fn c_exists(&self) -> bool {
+ fs::metadata(self).is_ok()
+ }
+
+ fn c_is_file(&self) -> bool {
+ fs::metadata(self).map(|m| m.is_file()).unwrap_or(false)
+ }
+
+ fn c_is_dir(&self) -> bool {
+ fs::metadata(self).map(|m| m.is_dir()).unwrap_or(false)
+ }
+
+ fn c_metadata(&self) -> io::Result<fs::Metadata> {
+ fs::metadata(self)
+ }
}
/// Ensure required test directories exist and are empty
fn setup() {}
test!(cargo_bench_simple {
+ if !::is_nightly() { return }
+
let p = project("foo")
.file("Cargo.toml", &basic_bin_manifest("foo"))
.file("src/foo.rs", r#"
{} target[..]release[..]foo-[..]
running 1 test
-test bench_hello ... bench: 0 ns/iter (+/- 0)
+test bench_hello ... bench: [..] 0 ns/iter (+/- 0)
test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured
});
test!(bench_tarname {
+ if !::is_nightly() { return }
+
let prj = project("foo")
.file("Cargo.toml" , r#"
[package]
{running} target[..]release[..]bin2[..]
running 1 test
-test run2 ... bench: 0 ns/iter (+/- 0)
+test run2 ... bench: [..] 0 ns/iter (+/- 0)
test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured
});
test!(cargo_bench_verbose {
+ if !::is_nightly() { return }
+
let p = project("foo")
.file("Cargo.toml", &basic_bin_manifest("foo"))
.file("src/foo.rs", r#"
{running} `[..]target[..]release[..]foo-[..] hello --bench`
running 1 test
-test bench_hello ... bench: 0 ns/iter (+/- 0)
+test bench_hello ... bench: [..] 0 ns/iter (+/- 0)
test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured
});
test!(many_similar_names {
+ if !::is_nightly() { return }
+
let p = project("foo")
.file("Cargo.toml", r#"
[package]
});
test!(cargo_bench_failing_test {
+ if !::is_nightly() { return }
+
let p = project("foo")
.file("Cargo.toml", &basic_bin_manifest("foo"))
.file("src/foo.rs", r#"
});
test!(bench_with_lib_dep {
+ if !::is_nightly() { return }
+
let p = project("foo")
.file("Cargo.toml", r#"
[project]
{running} target[..]release[..]baz-[..]
running 1 test
-test bin_bench ... bench: 0 ns/iter (+/- 0)
+test bin_bench ... bench: [..] 0 ns/iter (+/- 0)
test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured
{running} target[..]release[..]foo-[..]
running 1 test
-test lib_bench ... bench: 0 ns/iter (+/- 0)
+test lib_bench ... bench: [..] 0 ns/iter (+/- 0)
test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured
});
test!(bench_with_deep_lib_dep {
+ if !::is_nightly() { return }
+
let p = project("bar")
.file("Cargo.toml", r#"
[package]
{running} target[..]
running 1 test
-test bar_bench ... bench: 0 ns/iter (+/- 0)
+test bar_bench ... bench: [..] 0 ns/iter (+/- 0)
test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured
});
test!(external_bench_explicit {
+ if !::is_nightly() { return }
+
let p = project("foo")
.file("Cargo.toml", r#"
[project]
{running} target[..]release[..]bench-[..]
running 1 test
-test external_bench ... bench: 0 ns/iter (+/- 0)
+test external_bench ... bench: [..] 0 ns/iter (+/- 0)
test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured
{running} target[..]release[..]foo-[..]
running 1 test
-test internal_bench ... bench: 0 ns/iter (+/- 0)
+test internal_bench ... bench: [..] 0 ns/iter (+/- 0)
test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured
});
test!(external_bench_implicit {
+ if !::is_nightly() { return }
+
let p = project("foo")
.file("Cargo.toml", r#"
[project]
{running} target[..]release[..]external-[..]
running 1 test
-test external_bench ... bench: 0 ns/iter (+/- 0)
+test external_bench ... bench: [..] 0 ns/iter (+/- 0)
test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured
{running} target[..]release[..]foo-[..]
running 1 test
-test internal_bench ... bench: 0 ns/iter (+/- 0)
+test internal_bench ... bench: [..] 0 ns/iter (+/- 0)
test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured
});
test!(dont_run_examples {
+ if !::is_nightly() { return }
+
let p = project("foo")
.file("Cargo.toml", r#"
[project]
});
test!(pass_through_command_line {
+ if !::is_nightly() { return }
+
let p = project("foo")
.file("Cargo.toml", r#"
[package]
{running} target[..]release[..]foo-[..]
running 1 test
-test bar ... bench: 0 ns/iter (+/- 0)
+test bar ... bench: [..] 0 ns/iter (+/- 0)
test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured
{running} target[..]release[..]foo-[..]
running 1 test
-test foo ... bench: 0 ns/iter (+/- 0)
+test foo ... bench: [..] 0 ns/iter (+/- 0)
test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured
// Regression test for running cargo-bench twice with
// tests in an rlib
test!(cargo_bench_twice {
+ if !::is_nightly() { return }
+
let p = project("test_twice")
.file("Cargo.toml", &basic_lib_manifest("test_twice"))
.file("src/test_twice.rs", r#"
});
test!(lib_bin_same_name {
+ if !::is_nightly() { return }
+
let p = project("foo")
.file("Cargo.toml", r#"
[project]
{running} target[..]release[..]foo-[..]
running 1 test
-test [..] ... bench: 0 ns/iter (+/- 0)
+test [..] ... bench: [..] 0 ns/iter (+/- 0)
test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured
{running} target[..]release[..]foo-[..]
running 1 test
-test [..] ... bench: 0 ns/iter (+/- 0)
+test [..] ... bench: [..] 0 ns/iter (+/- 0)
test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured
});
test!(lib_with_standard_name {
+ if !::is_nightly() { return }
+
let p = project("foo")
.file("Cargo.toml", r#"
[package]
{running} target[..]release[..]bench-[..]
running 1 test
-test bench ... bench: 0 ns/iter (+/- 0)
+test bench ... bench: [..] 0 ns/iter (+/- 0)
test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured
{running} target[..]release[..]syntax-[..]
running 1 test
-test foo_bench ... bench: 0 ns/iter (+/- 0)
+test foo_bench ... bench: [..] 0 ns/iter (+/- 0)
test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured
});
test!(lib_with_standard_name2 {
+ if !::is_nightly() { return }
+
let p = project("foo")
.file("Cargo.toml", r#"
[package]
{running} target[..]release[..]syntax-[..]
running 1 test
-test bench ... bench: 0 ns/iter (+/- 0)
+test bench ... bench: [..] 0 ns/iter (+/- 0)
test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured
dir = p.url())));
});
-test!(bin_there_for_integration {
- let p = project("foo")
- .file("Cargo.toml", r#"
- [package]
- name = "foo"
- version = "0.0.1"
- authors = []
- "#)
- .file("src/main.rs", "
- #![feature(test)]
- extern crate test;
- fn main() { panic!(); }
- #[bench] fn main_bench(_b: &mut test::Bencher) {}
- ")
- .file("benches/foo.rs", r#"
- #![feature(test)]
- extern crate test;
- use std::process::Command;
- #[bench]
- fn bench_bench(_b: &mut test::Bencher) {
- let status = Command::new("target/release/foo").status().unwrap();
- assert_eq!(status.code(), Some(101));
- }
- "#);
-
- let output = p.cargo_process("bench").arg("-v").exec_with_output().unwrap();
- let output = str::from_utf8(&output.stdout).unwrap();
- assert!(output.contains("main_bench ... bench: 0 ns/iter (+/- 0)"),
- "no main_bench\n{}",
- output);
- assert!(output.contains("bench_bench ... bench: 0 ns/iter (+/- 0)"),
- "no bench_bench\n{}",
- output);
-});
-
test!(bench_dylib {
+ if !::is_nightly() { return }
+
let p = project("foo")
.file("Cargo.toml", r#"
[package]
{running} [..]target[..]release[..]bench-[..]
running 1 test
-test foo ... bench: 0 ns/iter (+/- 0)
+test foo ... bench: [..] 0 ns/iter (+/- 0)
test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured
{running} [..]target[..]release[..]foo-[..]
running 1 test
-test foo ... bench: 0 ns/iter (+/- 0)
+test foo ... bench: [..] 0 ns/iter (+/- 0)
test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured
{running} [..]target[..]release[..]bench-[..]
running 1 test
-test foo ... bench: 0 ns/iter (+/- 0)
+test foo ... bench: [..] 0 ns/iter (+/- 0)
test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured
{running} [..]target[..]release[..]foo-[..]
running 1 test
-test foo ... bench: 0 ns/iter (+/- 0)
+test foo ... bench: [..] 0 ns/iter (+/- 0)
test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured
});
test!(bench_twice_with_build_cmd {
+ if !::is_nightly() { return }
+
let p = project("foo")
.file("Cargo.toml", r#"
[package]
{running} target[..]release[..]foo-[..]
running 1 test
-test foo ... bench: 0 ns/iter (+/- 0)
+test foo ... bench: [..] 0 ns/iter (+/- 0)
test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured
{running} target[..]release[..]foo-[..]
running 1 test
-test foo ... bench: 0 ns/iter (+/- 0)
+test foo ... bench: [..] 0 ns/iter (+/- 0)
test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured
});
test!(bench_with_examples {
+ if !::is_nightly() { return }
+
let p = project("testbench")
.file("Cargo.toml", r#"
[package]
{running} `{dir}[..]target[..]release[..]testb1-[..] --bench`
running 1 test
-test bench_bench2 ... bench: 0 ns/iter (+/- 0)
+test bench_bench2 ... bench: [..] 0 ns/iter (+/- 0)
test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured
{running} `{dir}[..]target[..]release[..]testbench-[..] --bench`
running 1 test
-test bench_bench1 ... bench: 0 ns/iter (+/- 0)
+test bench_bench1 ... bench: [..] 0 ns/iter (+/- 0)
test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured
});
test!(test_a_bench {
+ if !::is_nightly() { return }
+
let p = project("foo")
.file("Cargo.toml", r#"
[project]
fn main() {}
"#)
.file("build.rs", r#"
- #![feature(exit_status)]
fn main() {
- std::env::set_exit_status(101);
+ std::process::exit(101);
}
"#);
assert_that(p.cargo_process("build").arg("-v"),
}
"#)
.file("build.rs", r#"
- #![feature(convert)]
use std::env;
use std::fs::File;
use std::io::prelude::*;
}
"#)
.file("build.rs", r#"
- #![feature(convert)]
use std::env;
use std::io::prelude::*;
use std::fs::File;
build = "build.rs"
"#)
.file("bar/build.rs", r#"
- #![feature(convert)]
use std::env;
use std::path::PathBuf;
}
test!(plugin_to_the_max {
+ if !::is_nightly() { return }
+
let foo = project("foo")
.file("Cargo.toml", r#"
[package]
});
test!(plugin_with_dynamic_native_dependency {
+ if !::is_nightly() { return }
+
let build = project("builder")
.file("Cargo.toml", r#"
[package]
test!(plugin_deps {
if disabled() { return }
+ if !::is_nightly() { return }
let foo = project("foo")
.file("Cargo.toml", r#"
test!(plugin_to_the_max {
if disabled() { return }
+ if !::is_nightly() { return }
let foo = project("foo")
.file("Cargo.toml", r#"
test!(plugin_with_extra_dylib_dep {
if disabled() { return }
+ if !::is_nightly() { return }
let foo = project("foo")
.file("Cargo.toml", r#"
build = 'build.rs'
"#)
.file("build.rs", &format!(r#"
- #![feature(convert)]
use std::env;
use std::path::PathBuf;
fn main() {{
use tempdir::TempDir;
use support::{execs, paths, cargo_dir};
+use support::paths::CargoPathExt;
use hamcrest::{assert_that, existing_file, existing_dir, is_not};
use cargo::util::{process, ProcessBuilder};
let mut contents = String::new();
File::open(&toml).unwrap().read_to_string(&mut contents).unwrap();
assert!(contents.contains(r#"authors = ["new-foo <new-bar>"]"#));
- assert!(!root.join("foo/.gitignore").exists());
+ assert!(!root.join("foo/.gitignore").c_exists());
});
test!(git_prefers_command_line {
.cwd(td.path())
.env("USER", "foo"),
execs().with_status(0));
- assert!(td.path().join("foo/.gitignore").exists());
+ assert!(td.path().join("foo/.gitignore").c_exists());
});
test!(subpackage_no_git {
authors = []
"#)
.file("src/main.rs", r#"
- #![feature(exit_status)]
- fn main() { std::env::set_exit_status(2); }
+ fn main() { std::process::exit(2); }
"#);
assert_that(p.cargo_process("run"),
authors = []
"#)
.file("bar/src/lib.rs", r#"
- #![feature(macro_rules)]
// make sure this file takes awhile to compile
macro_rules! f0( () => (1) );
macro_rules! f1( () => ({(f0!()) + (f0!())}) );
-#![feature(fs, fs_ext, path_ext, fs_time, fs_walk)]
-
-extern crate rustc_serialize;
+extern crate bufstream;
extern crate cargo;
+extern crate filetime;
extern crate flate2;
extern crate git2;
-extern crate bufstream;
extern crate hamcrest;
+extern crate libc;
+extern crate rustc_serialize;
extern crate tar;
+extern crate tempdir;
extern crate term;
extern crate url;
-extern crate tempdir;
+#[cfg(windows)] extern crate kernel32;
+#[cfg(windows)] extern crate winapi;
#[macro_use]
extern crate log;
fn rustc_host() -> String {
cargo::ops::rustc_version("rustc").unwrap().1
}
+
+fn is_nightly() -> bool {
+ let version_info = cargo::ops::rustc_version("rustc").unwrap().0;
+ version_info.contains("-nightly") || version_info.contains("-dev")
+}